package com.base.system.base.aop;

import cn.hutool.core.util.ArrayUtil;
import com.base.system.config.AppConfig;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

import java.lang.reflect.Modifier;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Component
@Aspect
@EnableAspectJAutoProxy
@Slf4j
public class SysConsoleLogAop {
    @Autowired
    private AppConfig appConfig;

    @Before("execution( * com.base.system.controller.*.*.*(..))")
    public void before(JoinPoint point) {
        printMethodParams(point);
    }

    /**
     * 打印类method的名称和参数
     *
     * @param point
     */
    private void printMethodParams(JoinPoint point) {
        if (point == null) return;
        String className = point.getTarget().getClass().getName();
        String simpleClassName = point.getTarget().getClass().getSimpleName();
        String methodName = point.getSignature().getName();
        //获取方法的参数值数组
        Object[] method_args = point.getArgs();
        try {
            // 获取方法参数名称
            String[] paramNames = getFieldsName(className, methodName);
            // 打印
            logParam(paramNames, method_args, className, methodName, simpleClassName);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 使用javassist来获取方法参数名称
     *
     * @param className  类名
     * @param methodName 方法名
     * @return
     * @throws Exception
     */
    private String[] getFieldsName(String className, String methodName) throws Exception {
        Class<?> clazz = Class.forName(className);
        String clazz_name = clazz.getName();
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(clazz);
        pool.insertClassPath(classPath);

        CtClass ctClass = pool.get(clazz_name);
        CtMethod ctMethod = ctClass.getDeclaredMethod(methodName);
        MethodInfo methodInfo = ctMethod.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) return null;
        String[] paramsArgsName = new String[ctMethod.getParameterTypes().length];
        int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;
        for (int i = 0; i < paramsArgsName.length; i++) paramsArgsName[i] = attr.variableName(i + pos);
        return paramsArgsName;
    }

    /**
     * 判断是否为基本类型：包括String
     *
     * @param clazz clazz
     * @return true：是;     false：不是
     */
    private boolean isPrimite(Class<?> clazz) {
        if (clazz.isPrimitive() || clazz == String.class) return true;
        else return false;
    }


    /**
     * 日志输出
     *
     * @param paramsArgsName
     * @param paramsArgsValue
     * @param className
     * @param methodName
     * @param simpleClassName
     */
    private void logParam(String[] paramsArgsName, Object[] paramsArgsValue, String className, String methodName, String simpleClassName) {
//        if(ArrayUtil.isEmpty(paramsArgsName) || ArrayUtil.isEmpty(paramsArgsValue)){
//            logger.info("该方法没有参数");
//            return;
//        }
        StringBuilder sb = new StringBuilder("\n\n项目：" + appConfig.getSysName() + appConfig.getSysVersion() + " controller report -------- ").append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append(" ----------------------\n\n");
        sb.append("Controller  : ").append(className).append(".(").append(simpleClassName).append(".java:1)");
        sb.append("\nMethod      : ").append(methodName);
        if (ArrayUtil.isNotEmpty(paramsArgsName)) sb.append("\nparameter   : ").append(methodName);
        for (int i = 0; i < paramsArgsName.length; i++) {
            //参数名
            String name = paramsArgsName[i];
            //参数值
            Object value = paramsArgsValue[i];
            sb.append(name + " = ");
            if (value == null) continue;
            if (value.getClass() != null && isPrimite(value.getClass())) sb.append(value + "  \n");
            else sb.append(value.toString() + "  \n");
        }
        sb.append("\n--------------------------------------------------- ").append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append(" ----------------------\n\n");
        log.info(sb.toString());
    }
}
